package operations;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;

import beans.Cell;

public class Operations {

	public static List<Cell> getMaxDeltaPath(Cell src, Cell dest) {
		List<Cell> path = new ArrayList<Cell>();
		Cell s = src;
		path.add(s);
		for (int i = 0; i < getDistance(src, dest); i++) {
			s = getNextCell(s, dest);
			path.add(s);
			if (s.equals(dest))
				break;
		}
		return path;
	}

	private static Cell getNextCell(Cell s, Cell dest) {
		int deltaX = getDeltaX(s, dest);
		int deltaY = getDeltaY(s, dest);
		if (deltaX == 0 && deltaY == 0)
			return s;
		if (deltaX == 0 && deltaY != 0)
			s = moveY(s, deltaY);
		else if (deltaY == 0 && deltaX != 0)
			s = moveX(s, deltaX);
		else if (Math.abs(deltaX) >= Math.abs(deltaY))
			s = moveX(s, deltaX);
		else if (Math.abs(deltaX) < Math.abs(deltaY))
			s = moveY(s, deltaY);
		return s;
	}

	public static List<Cell> getDeltaYPath(Cell src, Cell dest) {
		List<Cell> path = new ArrayList<Cell>();
		int deltaX = getDeltaX(src, dest);
		int deltaY = getDeltaY(src, dest);
		Cell s = src;
		while (deltaY != 0) {
			path.add(s);
			s = moveY(s, deltaY);
			deltaY = getDeltaY(s, dest);
		}
		while (deltaX != 0) {
			path.add(s);
			s = moveX(s, deltaX);
			deltaX = getDeltaX(s, dest);
		}
		return path;
	}

	public static List<Cell> getValidPath(Cell src, Cell dest) {
		List<Cell> validPath = new ArrayList<Cell>();
		validPath = getDeltaXPath(src, dest);
		if (!checkValidity(validPath))
			validPath = getDeltaYPath(src, dest);
		if (!checkValidity(validPath))
			validPath = getMaxDeltaPath(src, dest);
		validPath.add(dest);
		return validPath;
	}

	public static boolean checkValidity(List<Cell> path) {
		for (Cell c : path) {
			if (c.getX() < 0 || c.getY() < 0)
				return false;
			if (c.getX() >= 101 || c.getY() >= 101)
				return false;
		}
		return true;
	}

	public static List<Cell> getDeltaXPath(Cell src, Cell dest) {
		List<Cell> path = new ArrayList<Cell>();
		int deltaX = getDeltaX(src, dest);
		int deltaY = getDeltaY(src, dest);
		Cell s = src;
		while (deltaX != 0) {
			path.add(s);
			s = moveX(s, deltaX);
			deltaX = getDeltaX(s, dest);
		}
		while (deltaY != 0) {
			path.add(s);
			s = moveY(s, deltaY);
			deltaY = getDeltaY(s, dest);
		}
		return path;
	}

	private static int getDeltaX(Cell s, Cell d) {
		return d.getX() - s.getX();
	}

	private static int getDeltaY(Cell s, Cell d) {
		return d.getY() - s.getY();
	}

	private static Cell moveX(Cell cell, int deltaX) {
		if (deltaX > 0)
			return plusX(cell, 1);
		else
			return minusX(cell, 1);
	}

	private static Cell moveY(Cell cell, int deltaY) {
		if (deltaY > 0)
			return plusY(cell, 1);
		else
			return minusY(cell, 1);
	}

	public static List<Cell> getNeighbours(Cell cell, int hops) {
		List<Cell> neighbours = new ArrayList<Cell>();
		for (int i = 0; i < hops; i++) {
			neighbours.addAll(addLeftAndRightCells(plusY(cell, hops - i), i));
			neighbours.addAll(addLeftAndRightCells(minusY(cell, hops - i), i));
		}

		neighbours.addAll(addLeftAndRightCells(cell, hops));

		// remove the cell itself from it's neighbors list
		neighbours.removeAll(Collections.singleton(cell));

		// remove any duplicate cells from the list
		neighbours = new ArrayList<Cell>(new HashSet<Cell>(neighbours));

		List<Cell> validList = new ArrayList<Cell>();
		for (Cell c : neighbours) {
			if (isCellValid(c))
				validList.add(c);
		}
		return validList;
	}

	public static Boolean isCellValid(Cell cell) {
		if (cell.getX() >= 0 && cell.getX() < 101 && cell.getY() >= 0 && cell.getY() < 101) {
			return true;
		}
		return false;
	}

	private static List<Cell> addLeftAndRightCells(Cell c, int hops) {
		List<Cell> temp = new ArrayList<Cell>();
		if (hops > 0) {
			for (int i = 1; i <= hops; i++) {
				temp.add(plusX(c, i));
				temp.add(minusX(c, i));
			}
		}
		temp.add(c);
		return temp;
	}

	public static Cell plusY(Cell cell, int i) {
		return new Cell(cell.getX(), cell.getY() + i);
	}

	public static Cell plusX(Cell cell, int i) {
		return new Cell(cell.getX() + i, cell.getY());
	}

	public static Cell minusY(Cell cell, int i) {
		return new Cell(cell.getX(), cell.getY() - i);
	}

	public static Cell minusX(Cell cell, int i) {
		return new Cell(cell.getX() - i, cell.getY());
	}

	public static int getDistance(Cell first, Cell second) {
		if (first == null || second == null)
			return -1;
		return Math.abs(first.getX() - second.getX()) + Math.abs(first.getY() - second.getY());
	}
	
	public static Cell getRandomCell(){
		Cell cell=new Cell();
		cell.setX(new Random().nextInt(101));
		cell.setY(new Random().nextInt(101));
		return cell;
	}
	
	public static Color getRandomBrightColor() {
		Random random = new Random();
		final float hue = random.nextFloat();
		final float saturation = 1.0f; // 1.0 for brilliant, 0.0 for dull
		final float luminance = 1.0f; // 1.0 for brighter, 0.0 for black
		return Color.getHSBColor(hue, saturation, luminance);
	}

}
